package yredis

import (
	"context"
	"dgo/goutils/utils"
	"time"

	"github.com/go-redis/redis/v8"
)

/***
分布式锁
*/

var (
	ctx_background = context.Background()
)

type YredisLockRec struct {
	key    string
	keyval string
	expire time.Duration
}

type YredisLock struct {
	timer     *utils.Timer
	clt       *redis.Client
	lst       utils.GSyncMapGroup
	LockKeyID string
}

func NewYredisLock(clt *redis.Client) *YredisLock {
	rval := &YredisLock{
		LockKeyID: utils.RandKeyString(8),
	}
	rval.clt = clt
	return rval
}

func (this *YredisLock) Close() {
	if this.timer != nil {
		this.timer.Close()
	}
}

func (this *YredisLock) innerGetLockKeyID() string {
	rval := this.LockKeyID
	if len(rval) == 0 {
		rval = utils.RandKeyString(8)
	}
	return rval
}

func (this *YredisLock) ConfigKeepAlive(interval time.Duration) {
	if this.timer == nil {
		this.timer = utils.NewTimer()
		this.timer.OnTimer = func(tag interface{}) {
			this.lst.Range(func(key, value interface{}) bool {
				rec := value.(*YredisLockRec)
				if this.clt.Get(ctx_background, rec.key).Val() == rec.keyval {
					this.clt.Expire(ctx_background, rec.key, rec.expire)
				} else {
					this.lst.Remove(key)
				}
				return true
			})
		}
	}

	if interval > 0 {
		this.timer.ConfigInterval(interval, 0)
	} else {
		this.timer.Close()
	}
}

/*
**
如果已经锁定直接返回true
*/
func (this *YredisLock) CheckLock(key string, expire time.Duration) (ok bool) {
	if this.lst.Contain(key) {
		return true
	} else {
		keyval := this.innerGetLockKeyID()
		ok = this.clt.SetNX(ctx_background, key, keyval, expire).Val()
		if ok {
			this.lst.Set(key, &YredisLockRec{
				key:    key,
				keyval: keyval,
				expire: expire,
			})
		}
		return ok
	}
}

func (this *YredisLock) Lock(key string, expire time.Duration) (ok bool) {
	keyval := this.innerGetLockKeyID()
	ok = this.clt.SetNX(ctx_background, key, keyval, expire).Val()
	if ok {
		this.lst.Set(key, &YredisLockRec{
			key:    key,
			keyval: keyval,
			expire: expire,
		})
	}
	return ok
}

func (this *YredisLock) UnLock(key string) {
	obj := this.lst.Get(key, nil)
	if obj != nil {
		rec := obj.(*YredisLockRec)
		if this.clt.Get(ctx_background, key).Val() == rec.keyval {
			this.clt.Del(ctx_background, key)
		}
		this.lst.Remove(key)
	}
}
